#include "renderer.h"

#include "assist.h"

#include <QOpenGLContext>

#include <QDebug>

Renderer::Renderer(QString modelDiskPath, QWindow* window)
    : QObject(window),
      m_modelDiskPath(modelDiskPath),
      m_window(window),
      m_initialized(false),
      m_viewportResizeNecessary(true),
      m_scale(1.0)
{
    qDebug() << "[Renderer::Renderer()] creating QOpenGLContext";

    m_context = new QOpenGLContext(this);
    m_context->setFormat(window->requestedFormat());

    if (!m_context->create()) {
        qFatal("QOpenGLContext creation failed.");
    }

    Q_ASSERT(m_context->isValid());

    QSurfaceFormat actualFormat = m_context->format();

    qDebug() << "[Renderer::Renderer()] QOpenGLContext created, format -- "
             << QString("version: %1.%2").arg(actualFormat.majorVersion())
                .arg(actualFormat.minorVersion()).toStdString().c_str()
             << " depth buffer size: " << actualFormat.depthBufferSize();


    connect( window, SIGNAL(widthChanged(int)), SLOT(onResize()) );
    connect( window, SIGNAL(heightChanged(int)), SLOT(onResize()) );
}

void Renderer::render()
{
    if (!m_window->isExposed()) {
        qDebug() << "Renderer::render() -- window is not exposed.";
        return;
    }

    ensureInitialized();
    resizeIfNecessary();

    doRender();

    m_context->swapBuffers(m_window);
}

void Renderer::onResize()
{
    qDebug() << "Renderer::onResize()";

    m_viewportResizeNecessary = true;
    render();
}

void Renderer::ensureInitialized()
{
    if (!m_context->makeCurrent(m_window))
        qFatal("[Renderer::ensureInitialized()] -- failed to make context current");

    if (!m_initialized) {
        qDebug() << "[Renderer::ensureInitialized()] initializing.";

        initialize();
        m_initialized = true;
    } else {
//        qDebug() << "[Renderer::ensureInitialized()] already initialized.";
    }
}

void Renderer::setScale(float scale)
{
    m_scale = scale;
}

float Renderer::scale()
{
    return m_scale;
}

void Renderer::setOffset(QVector3D offset)
{
    m_offset = offset;
}

QVector3D Renderer::offset()
{
    return m_offset;
}

void Renderer::setXyTranslation(QVector2D translation)
{
    m_xyTranslation = translation;
}

QVector2D Renderer::xyTranslation()
{
    return m_xyTranslation;
}

void Renderer::setRotation(QMatrix4x4 rotation)
{
    m_rotation = rotation;
}

QMatrix4x4 Renderer::rotation()
{
    return m_rotation;
}

void Renderer::setProjection(QMatrix4x4 projection)
{
    m_projection = projection;
}

QMatrix4x4 Renderer::projection()
{
    return m_projection;
}

void Renderer::resizeIfNecessary()
{
    if (m_viewportResizeNecessary) {
        qDebug() << "[Renderer::resizeIfNecessary()] -- updating viewport geometry.";

        int w = m_window->width();
        int h = m_window->height();

        if (w > h) {
            glViewport(0, -(w - h)/2, w, w);
        } else {
            glViewport(-(h - w)/2, 0, h, h);
        }

//        double wToH = double(w)/h;

//        double d = 0.5;
//        double a = wToH;

//        double n = -1.0;
//        double f = 1.0;

//        double dfn = f - n;

        QMatrix4x4 projection;
//        projection.setRow(0, QVector4D(d/a,  0.0,   0.0,      0.0));
//        projection.setRow(1, QVector4D(0.0,    d,   0.0,      0.0));
//        projection.setRow(2, QVector4D(0.0,  0.0, f/dfn, -n*f/dfn));
//        projection.setRow(3, QVector4D(0.0,  0.0,   1.0,      0.0));

        projection.setRow(0, QVector4D(1.0, 0.0, 0.0, 0.0));
        projection.setRow(1, QVector4D(0.0, 1.0, 0.0, 0.0));
        projection.setRow(2, QVector4D(0.0, 0.0, 1.0, 0.0));
        projection.setRow(3, QVector4D(0.0, 0.0, 0.0, 1.0));

//        QMatrix4x4 projection;
//        projection.frustum(0, 0, double(w)/h, 1, 0.1, 1);

        m_projection = projection;

        m_viewportResizeNecessary = false;
    }
}
